//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#include <buola/bbox/protocol/cconnectionbase.h>

namespace buola { namespace bbox { namespace protocol {

CConnectionBase::CConnectionBase(io::PSocket pSocket,const std::string &pName)
    :   mHelloReceived(false)
    ,   mNextOwnSeq(0)
    ,   mNextPeerSeq(0)
    ,   mSocket(std::move(pSocket))
{
    SendHello(pName);
}

CConnectionBase::~CConnectionBase()
{
    ///\todo something else, I guess
}

uint32_t CConnectionBase::SendPacket(CPacket &pPacket)
{
    uint32_t lSeqNumber=mNextOwnSeq++;
    
    pPacket.Header().mSeqNumber=lSeqNumber;
    pPacket.Header().mSize=pPacket.Size();

    if(pPacket.Size()>200000)
    {
        size_t lSize=pPacket.Size();
        msg_info() << "sending multipart\n";
        start_timer();
        for(size_t lPos=0;lPos<lSize;lPos+=200000)
        {
            mSocket->SendPacket(pPacket.Data()+lPos,std::min((size_t)200000,lSize-lPos));
        }
        end_timer();
    }
    else
    {
        mSocket->Send(pPacket);
    }
    
    return lSeqNumber;
}

void CConnectionBase::SendError(const CPacket &pReplyTo,EError pError)
{
    bbox::protocol::CErrorPacket lReply(pReplyTo,pError);
    SendPacket(lReply);
}

std::unique_ptr<CPacket> CConnectionBase::ReadPacket(bool pBlock)
{
    while(std::unique_ptr<CPacket> lPacket=mPacketReader.GetPacket(*mSocket,pBlock))
    {
        std::size_t lPacketSize=lPacket->Size();
        
        if(mPartial)
        {
            if(mPartialPos+lPacketSize>mPartial->Size())
            {
                msg_warn() << "wrong partial packet size";
                mPartial.reset();
                continue;
            }
            
            memcpy(mPartial->Data()+mPartialPos,lPacket->Data(),lPacketSize);
            mPartialPos+=lPacketSize;
            if(mPartialPos<mPartial->Size())
                continue;
            lPacket=std::move(mPartial);
            mPartial.reset();
        }
        else if(lPacket->Size()!=lPacket->Header().mSize)
        {
            std::size_t lHeaderSize=lPacket->Header().mSize;

            if(lPacketSize>lHeaderSize)
            {
                msg_warn() << "wrong size in packet\n";
                continue;
            }
            
            mPartial=std::move(lPacket);
            mPartialPos=lPacketSize;
            mPartial->Resize(lHeaderSize);
            continue;
        }
        if(lPacket->Header().mSeqNumber!=mNextPeerSeq++)
        {
            msg_warn() << "wrong sequence number in packet!\n";
            continue;
        }

        if(!mHelloReceived)
        {
            if(lPacket->Header().Major()==EOpcode::HELLO)
            {
                std::string lPeerName;
                (*lPacket) >> lPeerName;
                
                mHelloReceived=true;

                OnHello(lPeerName);
            }
            else
            {
                msg_error() << "received a request before receiving hello\n";
            }
            continue;
        }

        return lPacket;
    }
    
    return nullptr;
}

void CConnectionBase::SendHello(const std::string &pName)
{
    CStackPacket lPacket(io::nHeader,EOpcode::HELLO);
    lPacket << pName;
    
    SendPacket(lPacket);
}

/*namespace protocol*/ } /*namespace bbox*/ } /*namespace buola*/ }
